package com.bosssoft.cloud.wechat.infrastructure.client;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.bosssoft.cloud.wechat.core.infra.enums.WxApiUrl;
import com.bosssoft.cloud.wechat.infrastructure.enums.ErrEnum;
import com.bosssoft.cloud.wechat.infrastructure.exception.SystemException;
import com.bosssoft.cloud.wechat.infrastructure.util.OkHttpUtil;
import com.squareup.okhttp.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * OkHttp Client类
 * 处理okhttp请求
 *
 * @author wcy create on 2021/7/15 16:45 下午
 */

@Slf4j
@Component
public class OkClient {

    public static final MediaType MT_JSON_UTF8 = MediaType.parse("application/json; charset=utf-8");

    private static OkHttpClient okHttpClient;

    @Autowired
    public void setOkHttpClient(OkHttpClient okHttpClient) {
        OkClient.okHttpClient = okHttpClient;
    }

    /**
     * builder模式链式调用请求的入口
     *
     * @param clazz clazz
     * @return Client builder
     */
    public OkClientBuilder newClient(Class<?> clazz) {
        return new OkClientBuilder(clazz);
    }

    public OkClientBuilder newClient() {
        return newClient(String.class);
    }

    /**
     * 构造者模式链式调用封装的okhttp请求
     */
    public static class OkClientBuilder {
        private final Request.Builder rb;
        private final Class<?> clazz;

        public OkClientBuilder(Class<?> clazz) {
            this.rb = new Request.Builder();
            this.clazz = clazz;
        }

        /**
         * 调用微信接口方法，无Request Body
         * 最后别忘了声明同步execute或异步enqueue
         *
         * @param wxApiUrl wx api
         * @param val      微信url param(拼接参数）
         */
        public OkClientBuilder runWx(WxApiUrl wxApiUrl, String... val) {
            return runWx(null, wxApiUrl, val);
        }

        public OkClientBuilder runWx(Object body, WxApiUrl wxApiUrl, String... val) {
            return runWx(JSON.toJSONString(body), wxApiUrl, val);
        }

        /**
         * 调用微信接口方法
         * 最后别忘了声明同步execute或异步enqueue
         *
         * @param wxApiUrl wx api
         * @param val      微信url param(拼接参数）
         * @param body     RequestBody
         */
        public OkClientBuilder runWx(String body, WxApiUrl wxApiUrl, String... val) {
            String url = val.length == 0 ?
                    wxApiUrl.getUrl() :
                    String.format(wxApiUrl.getUrl(), (Object[]) val);
            String type = wxApiUrl.getType();
            RequestBody requestBody = StrUtil.isEmpty(body) ?
                    type.equals("GET") || type.equals("HEAD") ?
                            null :
                            RequestBody.create(null, new byte[0]) :
                    RequestBody.create(MT_JSON_UTF8, body);
            return this
                    .url(url)
                    .method(type, requestBody);
        }

        public OkClientBuilder url(String url) {
            rb.url(url);
            return this;
        }

        public OkClientBuilder headers(Headers headers) {
            rb.headers(headers);
            return this;
        }

        public OkClientBuilder header(String name, String value) {
            rb.header(name, value);
            return this;
        }

        /**
         * 注：okhttp的Request.Builder()默认"GET"方法，实际get请求没必要调用此方法
         * 但为了规范和可读性，建议代码中体现请求类型
         */
        public OkClientBuilder get() {
            rb.get();
            return this;
        }

        public OkClientBuilder head() {
            rb.head();
            return this;
        }

        public OkClientBuilder post(String s) {
            if (StrUtil.isEmpty(s)) return post(RequestBody.create(null, new byte[0]));
            else return post(RequestBody.create(MT_JSON_UTF8, s));
        }

        public OkClientBuilder post(RequestBody body) {
            rb.post(body);
            return this;
        }

        public OkClientBuilder delete() {
            rb.delete();
            return this;
        }

        public OkClientBuilder delete(String s) {
            if (StrUtil.isEmpty(s)) return delete(RequestBody.create(null, new byte[0]));
            else return delete(RequestBody.create(MT_JSON_UTF8, s));
        }

        public OkClientBuilder delete(RequestBody body) {
            rb.delete(body);
            return this;
        }

        public OkClientBuilder put(String s) {
            if (StrUtil.isEmpty(s)) return put(RequestBody.create(null, new byte[0]));
            else return put(RequestBody.create(MT_JSON_UTF8, s));
        }

        public OkClientBuilder put(RequestBody body) {
            rb.put(body);
            return this;
        }

        public OkClientBuilder patch(String s) {
            if (StrUtil.isEmpty(s)) return patch(RequestBody.create(null, new byte[0]));
            else return patch(RequestBody.create(MT_JSON_UTF8, s));
        }

        public OkClientBuilder patch(RequestBody body) {
            rb.put(body);
            return this;
        }

        public OkClientBuilder method(String method, RequestBody body) {
            rb.method(method, body);
            return this;
        }

        /**
         * 执行同步请求
         * 出口
         */
        @SuppressWarnings("unchecked")
        public <T> T execute() {
            final Request request = rb.build();
            log.info("[OkHttp] ------- 新的OkHttp同步请求 ------->");
            OkHttpUtil.doLogRequest(request);
            String res = null;
            try {
                Response response = okHttpClient.newCall(request).execute();
                OkHttpUtil.doLogResponse(response);
                res = response.body().string();
            } catch (IOException e) {
                throw new SystemException(ErrEnum.B0002, e.toString());
            }
            log.info("[OkHttp] ------- OkHttp请求结束 ------->");
            return (T) JSON.parseObject(res, clazz);
        }

        /**
         * 执行异步请求
         * 出口
         */
        public void enqueue() {
            final Request request = rb.build();
            log.info("[OkHttp] ------- 新的OkHttp异步请求 ------->");
            OkHttpUtil.doLogRequest(request);
            okHttpClient.newCall(request).enqueue(new Callback() {
                @Override
                public void onFailure(Request request, IOException e) {
                    throw new SystemException(ErrEnum.B0002, e.toString());
                }

                @Override
                public void onResponse(Response response) {
                    if (!response.isSuccessful())
                        throw new SystemException(ErrEnum.B0002, "Unexpected code" + response);
                    OkHttpUtil.doLogResponse(response);
                }
            });
            log.info("[OkHttp] ------- OkHttp请求结束 ------->");
        }
    }

    /**
     * Simple get
     * 只做最基础的get操作，一般用不上，包括下面的simple方法
     */
    @Deprecated
    @SneakyThrows
    public String get(String url) {
        final Request request = new Request.Builder()
                .url(url)
                .build();
        Response response = okHttpClient.newCall(request).execute();
        return response.body().string();
    }

    /**
     * Simple post
     */
    @Deprecated
    @SneakyThrows
    public String post(String url, String json) {
        RequestBody body = RequestBody.create(MT_JSON_UTF8, json);
        Request request = new Request.Builder()
                .url(url)
                .post(body)
                .build();
        Response response = okHttpClient.newCall(request).execute();
        if (response.isSuccessful())
            return response.body().string();
        else
            throw new IOException("Unexpected code: " + response);
    }

    /**
     * Simple get enqueue
     */
    @Deprecated
    @SneakyThrows
    public void enqueue(String url) {
        final Request request = new Request.Builder()
                .url(url)
                .build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                e.printStackTrace();
            }

            @SneakyThrows
            @Override
            public void onResponse(Response response) {
                if (!response.isSuccessful())
                    throw new SystemException(ErrEnum.B0002, "Unexpected code" + response);
                log.info("[OkHttp] Response: {}", response.toString());
                log.info("\t Response body: {}", response.body().string());
            }
        });
    }

}
